#ifndef DRAGONBONES_JSON_DATA_PARSER_H
#define DRAGONBONES_JSON_DATA_PARSER_H

#include "DataParser.h"
#include "rapidjson/document.h"

DRAGONBONES_NAMESPACE_BEGIN

class ActionFrame
{
public:
    unsigned frameStart;
    std::vector<unsigned> actions;

    bool operator<(const ActionFrame& b) const { return frameStart < b.frameStart; }
};

class JSONDataParser : public DataParser
{
    DRAGONBONES_DISALLOW_COPY_AND_ASSIGN(JSONDataParser)

protected:
    inline static bool _getBoolean(const rapidjson::Value& rawData, const char* key, bool defaultValue)
    {
        if (rawData.HasMember(key))
        {
            const auto& value = rawData[key];
            if (value.IsBool())
            {
                return value.GetBool();
            }
            else if (value.IsString())
            {
                const std::string stringValue = value.GetString();
                if (stringValue == "0" || stringValue == "NaN" || stringValue == "" || stringValue == "false" ||
                    stringValue == "null" || stringValue == "undefined")
                {
                    return false;
                }

                return true;
            }
            else if (value.IsNumber())
            {
                return value.GetInt() != 0;
            }
        }

        return defaultValue;
    }

    inline static unsigned _getNumber(const rapidjson::Value& rawData, const char* key, unsigned defaultValue)
    {
        if (rawData.HasMember(key))
        {
            return rawData[key].GetUint();
        }

        return defaultValue;
    }

    inline static int _getNumber(const rapidjson::Value& rawData, const char* key, int defaultValue)
    {
        if (rawData.HasMember(key))
        {
            return rawData[key].GetInt();
        }

        return defaultValue;
    }

    inline static float _getNumber(const rapidjson::Value& rawData, const char* key, float defaultValue)
    {
        if (rawData.HasMember(key) && rawData[key].IsNumber())
        {
            return rawData[key].GetDouble();  // cocos can not support GetFloat();
        }

        return defaultValue;
    }

    inline static std::string _getString(const rapidjson::Value& rawData,
                                         const char* key,
                                         std::string_view defaultValue)
    {
        if (rawData.HasMember(key))
        {
            if (rawData[key].IsString())
            {
                return rawData[key].GetString();
            }

            return dragonBones::to_string(rawData[key].GetDouble());
        }

        return std::string{defaultValue};
    }

    inline static int _getParameter(const rapidjson::Value& rawData, std::size_t index, int defaultValue)
    {
        if (rawData.Size() > index)
        {
            return rawData[(int)index].GetInt();
        }

        return defaultValue;
    }

    inline static float _getParameter(const rapidjson::Value& rawData, std::size_t index, float defaultValue)
    {
        if (rawData.Size() > index)
        {
            return rawData[(int)index].GetDouble();
        }

        return defaultValue;
    }

    inline static std::string _getParameter(const rapidjson::Value& rawData,
                                            std::size_t index,
                                            std::string_view defaultValue)
    {
        if (rawData.Size() > index)
        {
            return rawData[(int)index].GetString();
        }

        return std::string{defaultValue};
    }

protected:
    unsigned _rawTextureAtlasIndex;
    std::vector<BoneData*> _rawBones;
    DragonBonesData* _data;
    ArmatureData* _armature;
    BoneData* _bone;
    SlotData* _slot;
    SkinData* _skin;
    MeshDisplayData* _mesh;
    AnimationData* _animation;
    TimelineData* _timeline;
    rapidjson::Value* _rawTextureAtlases;

private:
    int _defaultColorOffset;
    int _prevClockwise;
    float _prevRotation;
    Matrix _helpMatrixA;
    Matrix _helpMatrixB;
    Transform _helpTransform;
    ColorTransform _helpColorTransform;
    Point _helpPoint;
    std::vector<float> _helpArray;
    std::vector<std::int16_t> _intArray;
    std::vector<float> _floatArray;
    std::vector<std::int16_t> _frameIntArray;
    std::vector<float> _frameFloatArray;
    std::vector<std::int16_t> _frameArray;
    std::vector<std::uint16_t> _timelineArray;
    std::vector<const rapidjson::Value*> _cacheRawMeshes;
    std::vector<MeshDisplayData*> _cacheMeshes;
    std::vector<ActionFrame> _actionFrames;
    hlookup::string_map<const rapidjson::Value*> _weightSlotPose;
    hlookup::string_map<const rapidjson::Value*> _weightBonePoses;
    hlookup::string_map<std::vector<BoneData*>> _cacheBones;
    hlookup::string_map<std::vector<ActionData*>> _slotChildActions;

public:
    JSONDataParser()
        : _rawTextureAtlasIndex(0)
        , _rawBones()
        , _data(nullptr)
        , _armature(nullptr)
        , _bone(nullptr)
        , _slot(nullptr)
        , _skin(nullptr)
        , _mesh(nullptr)
        , _animation(nullptr)
        , _timeline(nullptr)
        , _rawTextureAtlases(nullptr)
        ,

        _defaultColorOffset(-1)
        , _prevClockwise(0)
        , _prevRotation(0.0f)
        , _helpMatrixA()
        , _helpMatrixB()
        , _helpTransform()
        , _helpColorTransform()
        , _helpPoint()
        , _helpArray()
        , _intArray()
        , _floatArray()
        , _frameIntArray()
        , _frameFloatArray()
        , _frameArray()
        , _timelineArray()
        , _cacheMeshes()
        , _cacheRawMeshes()
        , _actionFrames()
        , _weightSlotPose()
        , _weightBonePoses()
        , _cacheBones()
        , _slotChildActions()
    {}
    virtual ~JSONDataParser() {}

private:
    void _getCurvePoint(float x1,
                        float y1,
                        float x2,
                        float y2,
                        float x3,
                        float y3,
                        float x4,
                        float y4,
                        float t,
                        Point& result);
    void _samplingEasingCurve(const rapidjson::Value& curve, std::vector<float>& samples);
    void _parseActionDataInFrame(const rapidjson::Value& rawData, unsigned frameStart, BoneData* bone, SlotData* slot);
    void _mergeActionFrame(const rapidjson::Value& rawData,
                           unsigned frameStart,
                           ActionType type,
                           BoneData* bone,
                           SlotData* slot);
    unsigned _parseCacheActionFrame(ActionFrame& frame);

protected:
    virtual ArmatureData* _parseArmature(const rapidjson::Value& rawData, float scale);
    virtual BoneData* _parseBone(const rapidjson::Value& rawData);
    virtual ConstraintData* _parseIKConstraint(const rapidjson::Value& rawData);
    virtual SlotData* _parseSlot(const rapidjson::Value& rawData, int zOrder);
    virtual SkinData* _parseSkin(const rapidjson::Value& rawData);
    virtual DisplayData* _parseDisplay(const rapidjson::Value& rawData);
    virtual void _parsePivot(const rapidjson::Value& rawData, ImageDisplayData& display);
    virtual void _parseMesh(const rapidjson::Value& rawData, MeshDisplayData& mesh);
    virtual BoundingBoxData* _parseBoundingBox(const rapidjson::Value& rawData);
    virtual PolygonBoundingBoxData* _parsePolygonBoundingBox(const rapidjson::Value& rawData);
    virtual AnimationData* _parseAnimation(const rapidjson::Value& rawData);
    virtual TimelineData* _parseTimeline(
        const rapidjson::Value& rawData,
        const char* framesKey,
        TimelineType type,
        bool addIntOffset,
        bool addFloatOffset,
        unsigned frameValueCount,
        const std::function<unsigned(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount)>&
            frameParser);
    virtual void _parseBoneTimeline(const rapidjson::Value& rawData);
    virtual void _parseSlotTimeline(const rapidjson::Value& rawData);
    virtual unsigned _parseFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseTweenFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseActionFrame(const ActionFrame& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseZOrderFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseBoneAllFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseBoneTranslateFrame(const rapidjson::Value& rawData,
                                              unsigned frameStart,
                                              unsigned frameCount);
    virtual unsigned _parseBoneRotateFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseBoneScaleFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseSlotDisplayFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseSlotColorFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseSlotFFDFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual unsigned _parseIKConstraintFrame(const rapidjson::Value& rawData, unsigned frameStart, unsigned frameCount);
    virtual const std::vector<ActionData*>& _parseActionData(const rapidjson::Value& rawData,
                                                             ActionType type,
                                                             BoneData* bone,
                                                             SlotData* slot);
    virtual void _parseTransform(const rapidjson::Value& rawData, Transform& transform, float scale);
    virtual void _parseColorTransform(const rapidjson::Value& rawData, ColorTransform& color);
    virtual void _parseArray(const rapidjson::Value& rawData);
    virtual DragonBonesData* _parseDragonBonesData(const rapidjson::Value& rawData, float scale = 1.0f);
    virtual void _parseTextureAtlasData(const rapidjson::Value& rawData,
                                        TextureAtlasData& textureAtlasData,
                                        float scale = 1.0f);

public:
    virtual DragonBonesData* parseDragonBonesData(const char* rawData, float scale = 1.0f) override;
    virtual bool parseTextureAtlasData(const char* rawData,
                                       TextureAtlasData& textureAtlasData,
                                       float scale = 1.0f) override;
};

DRAGONBONES_NAMESPACE_END
#endif  // DRAGONBONES_JSON_DATA_PARSER_H
